package cn.fx.framework.web;

import cn.fx.framework.exception.BaseException;
import cn.fx.framework.utils.StringUtils;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.cookie.BasicCookieStore;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.*;
import org.apache.hc.core5.http.*;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.util.Timeout;

import javax.net.ssl.SSLContext;
import java.io.Closeable;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.*;


/**
 * 实现通过GET/POST访问WEB资源（包括http://与https:）
 * 返回结果有两种情况：
 * 1、CloseableHttpResponse，基本相应结果，包含web返回的Code、数据、原因短语；
 * 使用方法：get()与getHttps() 和 post()与postHttps()
 * 2、String 结果，通常返回的是JSON字符串
 * 使用方法：getForString()与postForString()
 * 调用方需要自行捕获异常
 *
 * @author fx
 * @since 2025-1-14 09:09:41
 **/

public class Http5Util {

    //region 1. HttpClient

    //region 连接配置
    private final static SocketConfig socketConfig = SocketConfig.custom()
            .setSoTimeout(Timeout.ofMilliseconds(5000))
            .build();
    private final static PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder
            .create()
            .setMaxConnTotal(1000)   //最大连接数
            .setMaxConnPerRoute(50)  //每个路由最大连接数，可理解为每url连接最大50个
            .setDefaultSocketConfig(socketConfig)
            .build();
    private final static RequestConfig requestConfig = RequestConfig.custom()
            .setConnectionRequestTimeout(Timeout.ofSeconds(8))
            .setResponseTimeout(Timeout.ofSeconds(8))
            .build();
    //endregion

    /**
     * 获取带Cookie的httpClient连接：支持默认的TLS传输层加密协议，即https支持。
     * 在build()时会自动加上DefaultClientTlsStrategy.createDefault()
     */
    private static CloseableHttpClient getHttpClient(BasicCookieStore basicCookieStore) {
        CloseableHttpClient httpClient = HttpClients
                .custom()
                .disableContentCompression()
                .setConnectionManager(connectionManager)
                .setDefaultRequestConfig(requestConfig)
                .setDefaultCookieStore(basicCookieStore)
                .build();
        return httpClient;
    }

    /**
     * 获取异步httpClient连接：httpAsyncClient
     */
    public static CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients
            .custom()
            .setConnectionManager(PoolingAsyncClientConnectionManagerBuilder
                    .create()
                    .setMaxConnTotal(1000)
                    .setMaxConnPerRoute(50)
                    .build())
            .setDefaultRequestConfig(requestConfig)
            .build();


    //endregion

    //region  2. GET部分
    public static CloseableHttpResponse get(String url) throws IOException, URISyntaxException {
        return get(url, null);
    }

    public static CloseableHttpResponse get(String url, List<NameValuePair> queryParams) throws IOException, URISyntaxException {
        return get(url, null, queryParams, null);
    }

    public static CloseableHttpResponse get(String url, Map<String, String> headers, List<NameValuePair> queryParams, BasicCookieStore basicCookieStore) throws IOException, URISyntaxException {
        return get(url, "GET", headers, queryParams, basicCookieStore);
    }

    /**
     * get 主方法，url支持http与https
     *
     * @param url
     * @param method
     * @param headers
     * @param queryParams
     * @param basicCookieStore
     * @return CloseableHttpResponse对象
     * @throws IOException
     * @throws URISyntaxException
     */
    public static CloseableHttpResponse get(String url, String method, Map<String, String> headers, List<NameValuePair> queryParams, BasicCookieStore basicCookieStore) throws IOException, URISyntaxException {
        CloseableHttpResponse response = null;
        if (!StringUtils.isNullOrEmpty(url) && url.length() > 6) {
            URI uri = new URIBuilder(new URI(url)).addParameters(queryParams).build();

            HttpUriRequestBase request = new HttpUriRequestBase(method, uri);
            if (!Objects.isNull(headers)) {
                BasicHeader[] head = mapToHeaders(headers);
                request.setHeaders(head);
            }
            response = getHttpClient(basicCookieStore).execute(request);
        }
        return response;
    }

    //endregion

    //region 2.1. getForString部分
    public static String getForString(String url) throws IOException, URISyntaxException, ParseException {
        return getForString(url, null);
    }

    public static String getForString(String url, List<NameValuePair> queryParams) throws IOException, URISyntaxException, ParseException {
        return getForString(url, null, queryParams, null);
    }

    public static String getForString(String url, Map<String, String> headers, List<NameValuePair> queryParams, BasicCookieStore basicCookieStore) throws IOException, URISyntaxException, ParseException {
        return getForString(url, "GET", headers, queryParams, basicCookieStore);
    }

    public static String getForString(String url, String method, Map<String, String> headers, List<NameValuePair> queryParams, BasicCookieStore basicCookieStore) throws IOException, ParseException, URISyntaxException {
        String result = "";
        CloseableHttpResponse response = get(url, method, headers, queryParams, basicCookieStore);
        if (response.getCode() / 100 == 2) { //当返回Code为2xx，表示成功
            result = EntityUtils.toString(response.getEntity());
        }
        closeQuietly(response);
        return result;
    }

    //endregion

    //region  3. POST部分
    public static CloseableHttpResponse post(String url, String strJSON) throws IOException, URISyntaxException {
        return post(url, "POST", strJSON);
    }

    public static CloseableHttpResponse post(String url, String method, String strJSON) throws IOException, URISyntaxException {
        return post(url, method, new StringEntity(strJSON, ContentType.APPLICATION_JSON));
    }

    public static CloseableHttpResponse post(String url, HttpEntity httpEntity) throws IOException, URISyntaxException {
        return post(url, "POST", httpEntity);
    }

    public static CloseableHttpResponse post(String url, Map<String, String> headers, HttpEntity httpEntity) throws IOException, URISyntaxException {
        return post(url, "POST", headers, httpEntity, null);
    }

    public static CloseableHttpResponse post(String url, String method, HttpEntity httpEntity) throws IOException, URISyntaxException {
        return post(url, method, null, httpEntity, null);
    }

    public static CloseableHttpResponse post(String url, String method, Map<String, String> headers, HttpEntity httpEntity) throws IOException, URISyntaxException {
        return post(url, method, headers, httpEntity, null);
    }

    /**
     * POST 主方法，支持http与https
     *
     * @param url
     * @param method
     * @param headers
     * @param httpEntity
     * @param basicCookieStore
     * @return
     * @throws IOException
     * @throws URISyntaxException
     */
    public static CloseableHttpResponse post(String url, String method, Map<String, String> headers, HttpEntity httpEntity, BasicCookieStore basicCookieStore) throws IOException, URISyntaxException {
        CloseableHttpResponse response = null;
        if (!StringUtils.isNullOrEmpty(url) && url.length() > 6) {
            URI uri = new URI(url);
            HttpUriRequestBase request = new HttpUriRequestBase(method, uri);

            if (!Objects.isNull(headers)) {
                BasicHeader[] head = mapToHeaders(headers);
                request.setHeaders(head);
            }
            request.setEntity(httpEntity);
            response = getHttpClient(basicCookieStore).execute(request);
        }

        return response;
    }


    //endregion

    //region 3.1 postForString部分

    /**
     * 向url地址POST提交json数据，支持http与https
     *
     * @param url
     * @param strJSON
     * @return 字符串，通常是JSON字符串
     * @throws IOException
     * @throws URISyntaxException
     * @throws ParseException
     */
    public static String postForString(String url, String strJSON) throws IOException, URISyntaxException, ParseException {
        return postForString(url, "POST", strJSON);
    }

    public static String postForString(String url, String method, String strJSON) throws IOException, URISyntaxException, ParseException {
        StringEntity stringEntity = null;
        if (!StringUtils.isNullOrEmpty(strJSON)) {
            stringEntity = new StringEntity(strJSON, ContentType.APPLICATION_JSON);
        }
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-type", "application/json; charset=UTF-8");
        return postForString(url, method, headers, stringEntity, null);
    }

    public static String postForString(String url, HttpEntity httpEntity) throws IOException, URISyntaxException, ParseException {
        return postForString(url, "POST", null, httpEntity);
    }

    public static String postForString(String url, String method, Map<String, String> headers, HttpEntity httpEntity) throws IOException, ParseException, URISyntaxException {
        return postForString(url, method, headers, httpEntity, null);
    }

    /**
     * 向url地址POST/PUT/PATCH/DELETE提交数据，支持http与https
     *
     * @param url
     * @param method
     * @param headers
     * @param httpEntity
     * @param basicCookieStore
     * @return
     * @throws IOException
     * @throws ParseException
     * @throws URISyntaxException
     */
    public static String postForString(String url, String method, Map<String, String> headers, HttpEntity httpEntity, BasicCookieStore basicCookieStore) throws IOException, ParseException, URISyntaxException {
        String result = "";
        CloseableHttpResponse response = null;
        response = post(url, method, headers, httpEntity, basicCookieStore);

        if (response.getCode() / 100 == 2) { //当返回Code为2xx，表示成功
            result = EntityUtils.toString(response.getEntity());
        }
        closeQuietly(response);

        return result;
    }
    //endregion

    //region 4、其他辅助部分
    private static BasicHeader[] mapToHeaders(Map<String, String> map) {
        BasicHeader[] headers = new BasicHeader[map.size()];
        int i = 0;
        for (Map.Entry<String, String> entry : map.entrySet()) {
            headers[i++] = new BasicHeader(entry.getKey(), entry.getValue());
        }
        return headers;
    }

    public static void closeQuietly(Closeable is) {
        if (is != null) {
            try {
                is.close();
            } catch (Exception ex) {
                throw new BaseException("关闭[" + is + "] 时出错，错误信息：" + ex.getMessage());
            }
        }
    }


    //endregion

    //region 5、main测试内容
    public static void main(String[] args) throws IOException, ParseException, URISyntaxException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {

        //测试Get访问Url

        List<NameValuePair> queryParams = new ArrayList<>();
        queryParams.add(new BasicNameValuePair("userId", "3"));

        //String str1 = getForString("https://juejin.cn/post/7228990409909108793", queryParams);

        //模拟get数据
        //String str1 = getForString("https://jsonplaceholder.typicode.com/posts/1", null);

        //模拟创建数据
        String strJson = "{\"category\":\"fruit\",\"name\":\"apple\",\"quantity\":\"100\"}";
        //String str1 = postForString("https://jsonplaceholder.typicode.com/posts",strJson);

        //模拟查询列表
        //String str1 = getForString("https://jsonplaceholder.typicode.com/posts");
        //String str1 = getForString("https://jsonplaceholder.typicode.com/posts",queryParams);

        //模拟更新数据
        String strJson2 = "{\"id\":\"3\",\"category\":\"fruit\",\"name\":\"apple\",\"quantity\":\"100\"}";
        String str1 = postForString("https://jsonplaceholder.typicode.com/posts/1","PUT", strJson2);

        //模拟部分更新数据
        String strJson3 = "{\"id\":\"3\",\"quantity\":\"30\"}";
        //String str1 = postForString("https://jsonplaceholder.typicode.com/posts/1","PATCH", strJson3);

        //模拟删除数据
        //String str1 = postForString("https://jsonplaceholder.typicode.com/posts/1","DELETE",null);

        System.out.println(str1);

    }

    //endregion
}